﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ComponentModel;
using DevExpress.Data.Filtering.Helpers;
using NHibernate;

namespace BuildHelper.Domain
{
    public class DomainObjectBindingListView<T> : BindingList<T>, IBindingListView, IDomainSaveObject where T : BaseDomainObject
    {
        #region CTOR
        public DomainObjectBindingListView(IList<T> list)
        {
            _UnfilteredList = list;
            ShowCurrent();
        }
        #endregion

        #region fields
        private IList<T> _UnfilteredList = new List<T>();
        private List<T> AcceptChangesItems = new List<T>();
        private List<T> DeletedItems = new List<T>();
        private string _Filter;
        #endregion

        #region Properties
        public IList<T> UnfilteredList
        {
            get { return _UnfilteredList; }
        }
       
        public string Filter
        {
            get
            {
                return _Filter;
            }
            set
            {
                if (_Filter != value)
                {
                    RaiseListChangedEvents = false;
                    if (value.IsNull())
                    {
                        this.Items.Clear();
                        foreach (T t in _UnfilteredList)
                            this.Items.Add(t);
                        _Filter = value;
                    }
                    else
                    {
                        try
                        {
                            ApplyFilter(value);
                            _Filter = value;
                        }
                        catch { }
                    }
                    RaiseListChangedEvents = true;
                    OnListChanged(new ListChangedEventArgs(ListChangedType.Reset, -1));
                }
            }
        }

        public ListSortDescriptionCollection SortDescriptions
        {
            get { return null; }
        }

        public bool SupportsAdvancedSorting
        {
            get { return false; }
        }

        public bool SupportsFiltering
        {
            get { return true; }
        }
        #endregion

        #region Public Methods
        public void ApplySort(ListSortDescriptionCollection sorts)
        {
           
        }

        /// <summary>
        /// Removes current filter
        /// </summary>
        public void RemoveFilter()
        {
            Filter = null;
        }

        /// <summary>
        /// Saves all changes
        /// </summary>
        /// <param name="session"></param>
        public void Save(ISession session)
        {
            ClearSaveState();
            try
            {
                SaveDeleted(session);
                SaveModified(session);
                SaveAdded(session);
            }
            catch (Exception ex)
            {
                ClearSaveState();
                throw ex;
            }
        }

        /// <summary>
        /// Saves all added records
        /// </summary>
        /// <param name="session"></param>
        public void SaveAdded(ISession session)
        {
            List<T> list = new List<T>();
            foreach (T g in this.UnfilteredList)
            {
                if (g.ObjectState == DomainObjectState.Added)
                {
                    g.Save(session);
                    list.Add(g);
                }
            }
            AcceptChangesItems.AddRange(list);
        }
        
        /// <summary>
        /// Saves all modified/ChildModified records
        /// </summary>
        /// <param name="session"></param>
        public void SaveModified(ISession session)
        {
            List<T> list = new List<T>();
            foreach (T g in this.UnfilteredList)
            {
                if (g.ObjectState == DomainObjectState.Modified ||
                    g.ObjectState == DomainObjectState.ChildModified ||
                    (g.ObjectState == DomainObjectState.Unchanged
                    && g.HasInnerModifications()))
                {
                    g.Save(session);
                    list.Add(g);
                }
            }
            AcceptChangesItems.AddRange(list);
        }
        
        /// <summary>
        /// Saves all deleted records
        /// </summary>
        /// <param name="session"></param>
        public void SaveDeleted(ISession session)
        {
            List<T> list = new List<T>();
            foreach (T g in this.UnfilteredList)
            {
                if (g.ObjectState == DomainObjectState.Deleted)
                {
                    g.Save(session);
                    list.Add(g);
                }
            }
            DeletedItems.AddRange(list);
        }

        /// <summary>
        /// Clears modified/deleted records status
        /// </summary>
        public void ClearSaveState()
        {
            AcceptChangesItems.Clear();
            DeletedItems.Clear();

        }

        /// <summary>
        /// Accept changes for all modified/added records and removes deleted records from collection
        /// </summary>
        public void AcceptChanges()
        {
            foreach (T t in AcceptChangesItems)
                t.AcceptChanges();
            foreach (T t in DeletedItems)
            {
                this.Remove(t);
                UnfilteredList.Remove(t);
            }
            ClearSaveState();
            OnListChanged(new ListChangedEventArgs(ListChangedType.Reset, -1));
        }

        /// <summary>
        /// Shows all records except deleted
        /// </summary>
        public void ShowCurrent()
        {
            Filter = "ObjectState <> 'Deleted'";
        }
     
        /// <summary>
        /// Shows only modified and ChildModified records
        /// </summary>
        public void ShowModified()
        {
            Filter = "ObjectState = 'Modified' OR ObjectState = 'ChildModified'";
        }

        /// <summary>
        /// Shows only deleted records
        /// </summary>
        public void ShowDeleted()
        {
            Filter = "ObjectState = 'Deleted'";
        }


        public void ShowAdded()
        {
            Filter = "ObjectState = 'Added'";
        }
        public void ShowUnchanged()
        {
            Filter = "ObjectState = 'Unchanged'";
        }
        public void ShowAll()
        {
            Filter = null;
        }
        #endregion

        #region Private Implementation
        protected override void ClearItems()
        {
            base.ClearItems();
            UnfilteredList.Clear();
        }
        protected override void InsertItem(int index, T item)
        {
            item.PropertyChanged += new PropertyChangedEventHandler(item_PropertyChanged);
            if (index == Items.Count)
                index = UnfilteredList.Count;
            UnfilteredList.Insert(index, item);
            ApplyFilter(Filter);
        }
       
        private void item_PropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            T item = (T)sender;
            int id = Items.IndexOf(item);
            OnListChanged(new ListChangedEventArgs(ListChangedType.ItemChanged, id, id));
        }

        protected override void RemoveItem(int index)
        {
            T item = UnfilteredList[index];
            item.PropertyChanged -= new PropertyChangedEventHandler(item_PropertyChanged);

            UnfilteredList.RemoveAt(index);
            if (Items.Contains(item))
                Items.Remove(item);
            OnListChanged(new ListChangedEventArgs(ListChangedType.Reset, -1));
        }
        protected override void OnListChanged(ListChangedEventArgs e)
        {
            if (e.ListChangedType == ListChangedType.Reset)
            {
                foreach (T item in UnfilteredList)
                {
                    item.PropertyChanged -= new PropertyChangedEventHandler(item_PropertyChanged);
                    item.PropertyChanged += new PropertyChangedEventHandler(item_PropertyChanged);
                }
                ApplyFilter(Filter);
            }
            else if(e.ListChangedType == ListChangedType.ItemChanged)
            {
                
                T o = Items[e.NewIndex];
                ApplyFilter(Filter, o);
                e = new ListChangedEventArgs(ListChangedType.ItemChanged,Items.IndexOf(o), e.NewIndex);
            }
            base.OnListChanged(e);
        }
        protected void ApplyFilter(string filter)
        {
            ExpressionEvaluator ev = new ExpressionEvaluator(TypeDescriptor.GetProperties(typeof(T)), filter);
            this.Items.Clear();
            foreach (T t in UnfilteredList)
            {
                if (ev.Fit(t))
                    this.Items.Add(t);
            }
        }
        protected void ApplyFilter(string filter, T item)
        {
            ExpressionEvaluator ev = new ExpressionEvaluator(TypeDescriptor.GetProperties(typeof(T)), filter);
            if (ev.Fit(item))
            {
                if (!this.Items.Contains(item))
                    this.Items.Add(item);
            }
            else
            {
                if (Items.Contains(item))
                    Items.Remove(item);
            }
        }

        #endregion
    }

    public interface IDomainSaveObject
    {
        void Save(ISession session);
    }
}
